﻿using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System;
using System.Collections.Generic;
using Apprenda.SaaSGrid;
using System.Data.SqlClient;
using System.Linq;
using SaaSKit.Taskr.Repository;
using Apprenda.SaaSGrid.Support;
using Apprenda.SaaSGrid.Subscription;

namespace SaaSKit.Taskr.Service
{
    public class CoreService : ICoreService, IAdminService
    {
        [Log]
        public Guid CreateTask(TaskDTO task)
        {
            Task _task = task.MapTo();
            
            if (_task.IsActive)
            {
                MeterStatus status = SubscriptionContext.Instance.IncrementLimitCounter(FeaturedActions.NumberOfTasks);
                if(status != MeterStatus.NotExhausted)
                {
                    throw new LimiterExhaustedException(FeaturedActions.NumberOfTasks, status, "You've exceeded the maximum number of active tasks allowed by your plan.");
                }
            }

            using (DataContext db = GetDataContext())
            {
                db.Tasks.InsertOnSubmit(_task);
                db.SubmitChanges();
            }

            return _task.Id;
        }

        [Log]
        public void UpdateTask(TaskDTO task)
        {
            using (DataContext db = GetDataContext())
            {
                Task _task = FindPersistedTask(db, task.Id);

                if (task.CompletedDate.HasValue && _task.IsActive)
                {
                    SubscriptionContext.Instance.DecrementLimitCounter(FeaturedActions.NumberOfTasks);
                }
                else if (!task.CompletedDate.HasValue && !_task.IsActive)
                {
                    MeterStatus status = SubscriptionContext.Instance.IncrementLimitCounter(FeaturedActions.NumberOfTasks);

                    if (status != MeterStatus.NotExhausted)
                    {
                        throw new LimiterExhaustedException(FeaturedActions.NumberOfTasks, status, "You've exceeded the maximum number of active tasks allowed by your plan.");
                    }
                }

                task.MapInto(_task);
                db.SubmitChanges();
            }
        }

        [Log]
        public void DeleteTask(Guid taskId)
        {
            using (DataContext db = GetDataContext())
            {
                Task _task = FindPersistedTask(db, taskId);

                if(_task.IsActive)
                {
                    SubscriptionContext.Instance.DecrementLimitCounter(FeaturedActions.NumberOfTasks);
                }

                db.Tasks.DeleteOnSubmit(_task);
                db.SubmitChanges();
            }
        }

        [Log]
        public TaskDTO GetTask(Guid taskId)
        {
            TaskDTO dto = new TaskDTO();

            using (DataContext db = GetDataContext())
            {
                Task _task = FindPersistedTask(db, taskId);
                _task.ActivityHistory = db.Activities.Where(a => a.TaskId == taskId).OrderBy(a => a.CreatedDate).ToList();
                dto.MapFrom(_task, true);
            }

            return dto;
        }

        [Log]
        public void PostActivity(ActivityDTO activity)
        {
            using(DataContext db = GetDataContext())
            {
                Task _task = FindPersistedTask(db, activity.TaskId);

                if (!_task.IsActive)
                {
                    throw new FaultException<ApplicationFaultDetail>
                    (
                        new ApplicationFaultDetail()
                        {
                            ErrorCode = ErrorCodes.Task_ActivityNotAllowedDueToInactiveTask,
                            EndUserMessage = "The current task has already been closed so no activity can be posted until it is re-opened."
                        },
                        "The current task has already been closed so no activity can be posted until it is re-opened."
                    );
                }

                db.Activities.InsertOnSubmit(activity.MapTo());
                db.SubmitChanges();
            }
        }

        [Log]
        [Secured(SecuredActions.ManageTasks, "You are not authorized to view tasks for users other than yourself.")]
        public IList<TaskDTO> ListAllTasks()
        {
            using(DataContext db = GetDataContext())
            {
                return ConvertTaskCollection(db.Tasks.OrderBy( t=> t.AssigneeId));
            }
        }

        [Log]
        [Secured(SecuredActions.ManageTasks, "You are not authorized to view tasks for users other than yourself.")]
        public IList<TaskDTO> ListAllActiveTasks()
        {
            using (DataContext db = GetDataContext())
            {
                return ConvertTaskCollection
                (
                    db.Tasks.Where(t => t.CompletedDate == null).OrderBy(t => t.AssigneeId)
                );
            }
        }

        [Log]
        [Secured(SecuredActions.ManageTasks, "You are not authorized to view tasks for users other than yourself.")]
        public IList<TaskDTO> ListAllCompletedTasks()
        {
            using (DataContext db = GetDataContext())
            {
                return ConvertTaskCollection
                (
                    db.Tasks.Where(t => t.CompletedDate != null).OrderByDescending(t => t.CompletedDate)
                );
            }
        }

        [Log]
        [Secured(SecuredActions.ManageTasks, "You are not authorized to view tasks for users other than yourself.")]
        public IList<TaskDTO> ListUnassignedTasks()
        {
            using (DataContext db = GetDataContext())
            {
                return ConvertTaskCollection
                (
                    db.Tasks.Where(t => t.AssigneeId == null).OrderBy(t => t.DueDate)
                );
            }
        }

        [Log]
        public IList<TaskDTO> ListTasks(Guid assigneeId)
        {
            using (DataContext db = GetDataContext())
            {
                return ConvertTaskCollection
                (
                    db.Tasks.Where(t => t.AssigneeId == assigneeId).OrderBy(t => t.DueDate)
                );
            }
        }

        [Log]
        public IList<TaskDTO> ListActiveTasks(Guid assigneeId)
        {
            using (DataContext db = GetDataContext())
            {
                return ConvertTaskCollection
                (
                    db.Tasks.Where(t => t.AssigneeId == assigneeId && t.CompletedDate == null).OrderBy(t => t.DueDate)
                );
            }
        }

        [Log]
        public IList<TaskDTO> ListCompletedTasks(Guid assigneeId)
        {
            using (DataContext db = GetDataContext())
            {
                return ConvertTaskCollection
                (
                    db.Tasks.Where(t => t.AssigneeId == assigneeId && t.CompletedDate != null).OrderByDescending(t => t.CompletedDate)
                );
            }
        }

        [Log]
        public SettingsDTO GetSettings()
        {
            SettingsDTO result = new SettingsDTO();

            using (DataContext db = GetDataContext())
            {
                Settings _settings = db.Settings.FirstOrDefault();

                if (_settings != null)
                {
                    result.MapFrom(_settings);
                }
            }

            return result;
        }

        [Log]
        [Secured(SecuredActions.ChangeSettings, "You are not authorized to change settings.")]
        public void UpdateSettings(SettingsDTO settings)
        {
            using (DataContext db = GetDataContext())
            {
                Settings _settings = db.Settings.FirstOrDefault();

                if (_settings == null)
                {
                    _settings = new Settings();
                    db.Settings.InsertOnSubmit(_settings);
                }

                settings.MapInto(_settings);
                db.SubmitChanges();
            }
        }

        #region Private Methods
        private DataContext GetDataContext()
        {
            return new DataContext(new SqlConnection(TenantContext.Current.ConnectionString));
        }

        private Task FindPersistedTask(DataContext dataContext, Guid taskId)
        {
            Task _task = dataContext.Tasks.FirstOrDefault(t => t.Id == taskId);

            if(_task == null)
            {
                throw new FaultException<ApplicationFaultDetail>
                (
                    new ApplicationFaultDetail()
                    {
                        ErrorCode = ErrorCodes.Task_NotFound,
                        EndUserMessage = "It appears that the task being referenced has been deleted."
                    },
                    string.Format("The task with id {0} has been deleted.", taskId)
                );
            }

            return _task;
        }

        private IList<TaskDTO> ConvertTaskCollection(IEnumerable<Task> collection)
        {
            IList<TaskDTO> result = new List<TaskDTO>();

            foreach (var task in collection)
            {
                TaskDTO taskDTO = new TaskDTO();
                taskDTO.MapFrom(task);
                result.Add(taskDTO);
            }

            return result;
        }
        #endregion
    }   
}
